
#include "area.h"
#include "avatar.h"
#include "commandTable.h"
#include "commandTable-default.h"
#include "display.h"
#include "handler.h"
#include "room.h"
#include "symphony.h"
#include "world.h"

/*
Cmd::Cmd( void ) {
  name( "" );
  return;
}

bool Cmd::execute( Creature* creature, const std::string& args ) {

  return true;
}
*/

CmdOedit::CmdOedit( void ) {
  name( "oedit" );
  level( BUILDER );
  syntax( "syntax: oedit <vnum>\n        oedit create <type> <vnum>\n\nValid types: trash container weapon armor key" );
  return;
}

bool CmdOedit::execute( Creature* creature, const std::string& args ) {
  std::vector<std::string> pieces = Regex::split( args, 3 );
  std::map<unsigned long,Object*>::iterator it;
  Area* area = NULL;
  Object* object = NULL;
  unsigned long vnum = 0;
  unsigned short type = OTYPE_UNDEFINED;

  if ( pieces.size() == 1 ) {
    vnum = estring(pieces[0]);
    // Get the area...
    if ( ( area = World::Instance().lookup( vnum ) ) == NULL ) {
      creature->send( "That vnum doesn't exist." );
      return false;
    }
    // Check permissions...
    if ( !World::Instance().hasPermission( area, (Avatar*)creature ) ) {
      creature->send( "You don't have permissions to that object." );
      return false;
    }
    // Make sure the Object exists...
    if ( ( it = area->objects().find( vnum ) ) == area->objects().end() ) {
      creature->send( "That object doesn't exist." );
      return false;
    }
    // Make sure no one else is editing the object...
    for ( std::map<std::string,Avatar*>::iterator a_it = World::Instance().getAvatars().begin(); a_it != World::Instance().getAvatars().end(); ++a_it ) {
      if ( a_it->second->mode() == MODE_OEDIT && a_it->second->oedit() == it->second ) {
        creature->send( "Sorry, %s is currently editing %s (object %lu).", creature->seeName( ((Creature*)a_it->second) ).c_str(), it->second->shortname().c_str(), it->second->vnum() );
        return false;
      }
    }
    // All looks well; send them to oedit...
    creature->oedit( it->second );
    creature->mode( MODE_OEDIT );
    creature->pushHandler( new OeditHandler( creature ) );
    creature->send( "You're editing object %lu.", creature->oedit()->vnum() );
    return true;
  }

  if ( pieces.size() == 3 && Regex::strPrefix( pieces[0], "create" ) ) {
    vnum = estring(pieces[2]);
    // Get the area...
    if ( ( area = World::Instance().lookup( vnum ) ) == NULL ) {
      creature->send( "That vnum doesn't exist." );
      return false;
    }
    // Check permissions...
    if ( !World::Instance().hasPermission( area, (Avatar*)creature ) ) {
      creature->send( "You don't have permissions to that object." );
      return false;
    }
    // Make sure the Object doesn't already exist...
    if ( ( it = area->objects().find( vnum ) ) != area->objects().end() ) {
      creature->send( "That object already exists." );
      return false;
    }
    // Check for a valid object type...
    if ( ( type = Object::getType( pieces[1] ) ) == OTYPE_UNDEFINED ) {
      creature->send( "Invalid type argument.  Valid types are: trash, container, weapon, armor" );
      return false;
    }
    // Everything checks out; let's make us a new Object!
    if ( ( object = Object::create( area, vnum, type ) ) == NULL ) {
      creature->send( "There was an error creating the object." );
      return false;
    }
    creature->send( "Object %lu created successfully.", object->vnum() );
    creature->mode( MODE_OEDIT );
    creature->oedit( object );
    creature->pushHandler( new OeditHandler( creature ) );
    return true;
  }

  creature->send( syntax() );
  return false;
}

CmdOlist::CmdOlist( void ) {
  name( "olist" );
  level( BUILDER );
  syntax( "syntax: olist <areaID>\n        olist <first vnum> <last vnum>\n        olist <string>\n        olist /<regex>\n  [Enclose 'search string' or '/regular expression' in quotes if it contains any spaces]" );
  return;
}

bool CmdOlist::execute( Creature* creature, const std::string& args ) {
  std::vector<std::string> pieces = Regex::split( args, 2 );
  std::vector<Object*> objects;
  Area* area = NULL;
  unsigned long low = 0;
  unsigned long high = 0;
  std::string search;
  std::string output;
  char buffer[MAX_BUFFER];

  if ( pieces.size() == 1 ) {
    if ( Regex::match( "^[0-9]+$", pieces[0] ) ) {
      // We got an areaID...
      if ( ( area = World::Instance().findArea( estring(pieces[0]) ) ) == NULL ) {
        creature->send( "That area couldn't be found." );
        return false;
      }
      for ( std::map<unsigned long,Object*>::iterator o_it = area->objects().begin(); o_it != area->objects().end(); ++o_it ) {
        objects.push_back( o_it->second );
      }
    } else {
      if ( pieces[0][0] == '/' ) {
        pieces[0].erase( 0, 1 );
        // This search is a regex...
        for ( std::set<Area*,area_comp>::iterator a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); ++a_it ) {
          for ( std::map<unsigned long,Object*>::iterator o_it = (*a_it)->objects().begin(); o_it != (*a_it)->objects().end(); ++o_it ) {
            if ( o_it->second->matchesKeyword( pieces[0] ) ) {
              objects.push_back( o_it->second );
            }
          }
        }
      } else {
        search = Regex::lower(pieces[0]);
        // We got a search string...
        for ( std::set<Area*,area_comp>::iterator a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); ++a_it ) {
          for ( std::map<unsigned long,Object*>::iterator o_it = (*a_it)->objects().begin(); o_it != (*a_it)->objects().end(); ++o_it ) {
            if ( o_it->second->matchesKeyword( search ) ) {
              objects.push_back( o_it->second );
            }
          }
        }
      }
    }
  } else if ( pieces.size() == 2 ) {
    /* We're looking for a vnum range here */
    // Grab our range values...
    low = estring(pieces[0]);
    high = estring(pieces[1]);
    // Check our range...
    if ( !high || low >= high ) {
      creature->send( "Invalid vnum range." );
      return false;
    }
    if ( low+400 < high ) {
      creature->send( "The maximum vnum range is 400." );
      return false;
    }
    // Grab the rooms...
    for ( std::set<Area*,area_comp>::iterator a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); ++a_it ) {
      for ( std::map<unsigned long,Object*>::iterator o_it = (*a_it)->objects().begin(); o_it != (*a_it)->objects().end(); ++o_it ) {
        if ( o_it->second->vnum() >= low && o_it->second->vnum() <= high ) {
          objects.push_back( o_it->second );
        }
      }
    }
  } else {
    creature->send( syntax() );
    return false;
  }

  if ( objects.empty() ) {
    creature->send( "No matches for \"%s\"", pieces[0].c_str() );
    return false;
  }

  output.append( " [{y vnum{x] {gname{x\n -------------------\n" );
  for ( std::vector<Object*>::iterator it = objects.begin(); it != objects.end(); ++it ) {
    sprintf( buffer, " [{y%5lu{x] %s{x\n", (*it)->vnum(), (*it)->shortname().c_str() );
    output.append( buffer );
  }

  creature->send( output );
  return true;
}

CmdOload::CmdOload( void ) {
  name( "oload" );
  level( BUILDER );
  syntax( "syntax: oload <vnum>" );
  return;
}

bool CmdOload::execute( Creature* creature, const std::string& args ) {
  unsigned long vnum = estring(args);
  std::map<unsigned long,Object*>::iterator o_it;
  Object* object = NULL;

  if ( args.empty() ) {
    creature->send( syntax() );
    return false;
  }

  for ( std::set<Area*,area_comp>::iterator a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); ++a_it ) {
    if ( ( o_it = (*a_it)->objects().find( vnum ) ) != (*a_it)->objects().end() ) {
      object = Object::create( o_it->second );
      if ( object == NULL ) {
        creature->send( "Oload failed." );
        return false;
      }
      World::Instance().insert( object );
      creature->add( object );
      creature->send( "You load %s.", object->shortname().c_str() );
      creature->room()->send_cond( "$p has created $o.", creature, object );
      return true;
    }
  }

  creature->send( "There is no object of that vnum." );
  return false;
}

CmdOoc::CmdOoc( void ) {
  name( "ooc" );
  return;
}

bool CmdOoc::execute( Creature* creature, const std::string& args ) {
  std::string text = Regex::trim( args );

  if ( args.empty() ) {
    creature->channel( CHAN_OOC, !creature->channel( CHAN_OOC ) );
    if ( creature->channel( CHAN_OOC ) ) {
      creature->send( "OOC channel [{GON{x]" );
      return true;
    } else {
      creature->send( "OOC channel [{rOFF{x]" );
      return true;
    }
  }

  if ( !creature->channel( CHAN_OOC ) ) {
    creature->send( "You currently have the OOC channel off." );
    return false;
  }

  for ( std::map<std::string,Avatar*>::iterator it = World::Instance().getAvatars().begin(); it != World::Instance().getAvatars().end(); ++it ) {
    if ( it->second->status() == CONNECTED && it->second != creature ) {
      if ( it->second->channel( CHAN_OOC ) ) {
        it->second->send( "%s OOC's{x \"{W%s{x\"", it->second->seeName( creature, true ).c_str(), text.c_str() );
      }
    }
  }
  creature->send( "You OOC{x \"{W%s{x\"", text.c_str() );

  return true;
}

CmdOpen::CmdOpen( void ) {
  name( "open" );
  syntax( "syntax: open <direction>" );
  return;
}

bool CmdOpen::execute( Creature* creature, const std::string& args ) {
  Exit* exit = NULL;
  Exit* inv = NULL;

  if ( ( exit = creature->room()->exit( args ) ) == NULL ) {
    creature->send( "There is no door in that direction." );
    return false;
  }

  if ( !exit->flag( EFLAG_CLOSED ) ) {
    creature->send( "It's already open." );
    return false;
  }

  if ( exit->flag( EFLAG_LOCKED ) ) {
    creature->send( "It's locked." );
    return false;
  }

  exit->flag( EFLAG_CLOSED, false );
  creature->send( "You open the door." );
  creature->room()->send( "$p opened the way $e.", creature, exit );
  if ( ( inv = exit->target()->exit( Exit::inverse( exit->direction() ) ) ) != NULL ) {
    exit->target()->send( "The way $e has been opened.", NULL, inv );
  }

  return true;
}

CmdPedit::CmdPedit( void ) {
  name( "pedit" );
  level( GOD );
  return;
}

bool CmdPedit::execute( Creature* creature, const std::string& args ) {
  std::string name = Regex::trim( args );
  Creature* target = NULL;

  // Check for a player name...
  if ( name.empty() ) {
    creature->send( "You must specify a player to edit." );
    return false;
  }

  // Find the player...
  if ( ( target = World::Instance().findAvatar( name ) ) == NULL ) {
    creature->send( "That player isn't around." );
    return false;
  }

  // Make sure noone else is in Pedit mode for the player...
  // Make sure no one else is editing the object...
  for ( std::map<std::string,Avatar*>::iterator a_it = World::Instance().getAvatars().begin(); a_it != World::Instance().getAvatars().end(); ++a_it ) {
    if ( a_it->second->mode() == MODE_PEDIT && a_it->second->pedit() == target ) {
      creature->send( "Sorry, %s is currently editing %s.", creature->seeName( ((Creature*)a_it->second) ).c_str(), target->name().c_str() );
      return false;
    }
  }

  if ( creature->canAlter( target ) || creature->level() >= CREATOR ) {
    creature->send( "You're editing %s.", target->name().c_str() );
    creature->mode( MODE_PEDIT );
    creature->pedit( target );
    ((Avatar*)creature)->pushHandler( new PeditHandler( (Avatar*)creature ) );
    return true;
  } else {
    creature->send( "You can't edit %s.", target->name().c_str() );
    return false;
  }

  return true;
}

CmdPut::CmdPut( void ) {
  name( "put" );
  syntax( "syntax: put <object> <container>\n        put all <container>" );
  return;
}

bool CmdPut::execute( Creature* creature, const std::string& args ) {
  std::vector<std::string> pieces = Regex::split( args, 2 );
  std::list<Object*> temp;
  Object* foo = NULL;
  ObjContainer* container = NULL;
  Object* object = NULL;

  if ( args.empty() ) {
    creature->send( syntax() );
    return false;
  }

  if ( pieces.size() == 2 ) {
    if ( pieces[0] == "all" ) {
      if ( ( foo = creature->findObject( pieces[1], SEARCH_CARRY | SEARCH_ROOM ) ) == NULL ) {
        creature->send( "You don't see that here." );
        return false;
      }
      if ( foo->type() == OTYPE_CONTAINER ) {
        container = (ObjContainer*)foo;
      } else {
        creature->send( "That's not a container." );
        return false;
      }
      if ( creature->objects().empty() ) {
        creature->send( "You're not carrying anything that you can put in %s.", container->shortname().c_str() );
        return false;
      }
      for ( std::list<Object*>::iterator it = creature->objects().begin(); it != creature->objects().end(); ++it ) {
        if ( *it != container ) {
          temp.push_back( *it );
        }
      }
      for ( std::list<Object*>::iterator it = temp.begin(); it != temp.end(); ++it ) {
        creature->remove( *it );
        container->add( *it );
      }
      temp.clear();
      creature->send( "You put everything in %s.", container->shortname().c_str() );
      creature->room()->send_cond( "$p puts some things in $o.", creature, container );
      return true;
    } else {
      if ( ( foo = creature->findObject( pieces[1], SEARCH_CARRY | SEARCH_ROOM ) ) == NULL ) {
        creature->send( "You don't see that here." );
        return false;
      }
      if ( foo->type() == OTYPE_CONTAINER ) {
        container = (ObjContainer*)foo;
      } else {
        creature->send( "That's not a container." );
        return false;
      }
      if ( ( object = creature->findObject( pieces[0] ) ) == NULL ) {
        creature->send( "You don't have that." );
        return false;
      }
      if ( container == object ) {
        creature->send( "You can't fold %s inside itself.", container->shortname().c_str() );
        return false;
      }
      creature->remove( object );
      container->add( object );
      creature->send( "You put %s in %s.", object->shortname().c_str(), container->shortname().c_str() );
      creature->room()->send( "$p puts $o into $O.", creature, object, container );
      return true;
    }
  }

  creature->send( syntax() );
  return false;
}

